#include "ReflectableMap.h"

#include "Containers/ReflectableForwardLinkedList.h"
#include "Containers/ReflectableVector.h"

#include "Reflection/PairType.h"
#include "Reflection/ForwardLinkedListType.h"

CReflectableMap::CReflectableMap(CForwardLinkedListGeneralized *InForwardLinkedListType)
{
    Capacity=16;
    Data.Resize(InForwardLinkedListType, Capacity);
}

CReflectableMap::CReflectableMap(CForwardLinkedListGeneralized *InForwardLinkedListType, const CReflectableMap &InMap)
    : Data(InForwardLinkedListType, InMap.Data), Size(InMap.Size), Capacity(InMap.Capacity)
{
}

void CReflectableMap::Add(CForwardLinkedListGeneralized *InForwardLinkedListType, void *InPair)
{
    CPairGeneralized* PairType = (CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType;
    //CType* KeyType = PairType->GetKeyType();
    uint64_t Hash = PairType->GetKeyHash(InPair);
	uint64_t ArrayIndex = Hash & (Capacity - 1);

    CReflectableForwardLinkedList * List = (CReflectableForwardLinkedList *)Data.At(InForwardLinkedListType,ArrayIndex);
    
    for (auto& Node : *List)
    {
        if(PairType->IsKeyEqual(Node.GetData(), InPair))
        {
            PairType->EraseValue(Node.GetData());
            PairType->CloneValue(InPair,Node.GetData());
            MakesureCapacity(InForwardLinkedListType);
            return;
        }
    }

    CReflectableForwardLinkedListNode * NewNode = new CReflectableForwardLinkedListNode(PairType,InPair );
    List->InsertAtHead(NewNode);
    Size++;
    MakesureCapacity(InForwardLinkedListType);
}

void CReflectableMap::Add(CForwardLinkedListGeneralized *InForwardLinkedListType, void *InKey, void *InValue)
{
    CPairGeneralized* PairType = (CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType;
    //CType* KeyType = PairType->GetKeyType();
    uint64_t Hash = PairType->GetKeyHash(InKey);
    uint64_t ArrayIndex = Hash & (Capacity - 1);

    CReflectableForwardLinkedList * List = (CReflectableForwardLinkedList *)Data.At(InForwardLinkedListType,ArrayIndex);
    
    CType* KeyType = PairType->GetKeyType().BasicType;
    CType* ValueType = PairType->GetValueType().BasicType;
    for (auto& Node : *List)
    {
        if(PairType->IsKeyEqual(Node.GetData(), InKey))
        {
            void* OldValue = PairType->GetValuePtr(Node.GetData());
            ValueType->EraseInstance(OldValue);
            ValueType->CloneInstance(InValue,OldValue);

            return;
        }
    }
    
    CReflectableForwardLinkedListNode * NewNode = new CReflectableForwardLinkedListNode();
    NewNode->Data = malloc(PairType->GetSize());
    PairType->CloneKeyAndValue(InKey,InValue,NewNode->Data);

    List->InsertAtHead(NewNode);
    Size++;
    MakesureCapacity(InForwardLinkedListType);
}

void CReflectableMap::Remove(CForwardLinkedListGeneralized *InForwardLinkedListType, void *InKey)
{
    CPairGeneralized* PairType = (CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType;
    uint64_t Hash = PairType->GetKeyHash(InKey);
    uint64_t ArrayIndex = Hash & (Capacity - 1);

    CReflectableForwardLinkedList * List = (CReflectableForwardLinkedList *)Data.At(InForwardLinkedListType,ArrayIndex);
    
    CReflectableForwardLinkedListNode * FormerNode=nullptr;
    for (auto& Node : *List)
    {
        if(PairType->IsKeyEqual(Node.GetData(), InKey))
        {
            PairType->EraseValue(Node.GetData());
            if(!FormerNode)
            {
                List->RemoveHead(PairType);
            }
            else
            {
                List->RemoveNextNode(PairType,FormerNode);
            }
            Size--;
            return;
        }

        FormerNode = &Node;
    }
}

void *CReflectableMap::FindValue(CForwardLinkedListGeneralized *InForwardLinkedListType, void *InKey) const
{
    CPairGeneralized* PairType = (CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType;
    uint64_t Hash = PairType->GetKeyHash(InKey);
    uint64_t ArrayIndex = Hash & (Capacity - 1);

    CReflectableForwardLinkedList * List = (CReflectableForwardLinkedList *)Data.At(InForwardLinkedListType,ArrayIndex);
    
    for (auto& Node : *List)
    {
        if(PairType->IsKeyEqual(Node.GetData(), InKey))
        {
            return PairType->GetValuePtr(Node.GetData());
        }
    }
    
    assert(false && "Can't find such key" );

    return nullptr;
}

bool CReflectableMap::Contains(CForwardLinkedListGeneralized *InForwardLinkedListType, void *InKey) const
{
    CPairGeneralized* PairType = (CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType;
    uint64_t Hash = PairType->GetKeyHash(InKey);
    uint64_t ArrayIndex = Hash & (Capacity - 1);

    CReflectableForwardLinkedList * List = (CReflectableForwardLinkedList *)Data.At(InForwardLinkedListType,ArrayIndex);
    
    for (auto& Node : *List)
    {
        if(PairType->IsKeyEqual(Node.GetData(), InKey))
        {
            return true;
        }
    }
    return false;
}

TVector<void *> CReflectableMap::NativeGetKeys(CForwardLinkedListGeneralized *InForwardLinkedListType) const
{
    TVector<void *> Result;
    Result.Reserve(Size);

    for(uint64_t i=0;i<Data.Num();i++)
    {
        CReflectableForwardLinkedList * List = (CReflectableForwardLinkedList *)Data.At(InForwardLinkedListType,i);
        for (auto& Node : *List)
        {
            void * Key = ((CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType)->GetKeyPtr(Node.GetData());
            Result.Add(Key);
        }
    }

    return Result;
}

TVector<void *> CReflectableMap::NativeGetValues(CForwardLinkedListGeneralized *InForwardLinkedListType) const
{
    TVector<void *> Result;
    Result.Reserve(Size);

    for(uint64_t i=0;i<Data.Num();i++)
    {
        CReflectableForwardLinkedList * List = (CReflectableForwardLinkedList *)Data.At(InForwardLinkedListType,i);
        for (auto& Node : *List)
        {
            void * Value = ((CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType)->GetValuePtr(Node.GetData());
            Result.Add(Value);
        }
    }

    return Result;
}

void CReflectableMap::Clear(CForwardLinkedListGeneralized *InForwardLinkedListType)
{
    Data.Clear(InForwardLinkedListType,false);
    Data.Resize(InForwardLinkedListType,16);
    Size = 0;
}

uint64_t CReflectableMap::GetHashCode(CForwardLinkedListGeneralized *InForwardLinkedListType) const
{
    uint64_t Result = 0;
    for(uint64_t i=0;i<Data.Num();i++)
    {
        CReflectableForwardLinkedList * List = (CReflectableForwardLinkedList *)Data.At(InForwardLinkedListType,i);
        for (auto& Node : *List)
        {
            Result += ((CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType)->GetKeyHash(Node.GetData());
        }
    }
    return Result;
}

bool CReflectableMap::IsEqual(CForwardLinkedListGeneralized *InForwardLinkedListType, const CReflectableMap &InMap) const
{
    if(Size != InMap.Size)
    {
        return false;
    }

    for(uint64_t i=0;i<Data.Num();i++)
    {
        CReflectableForwardLinkedList * List = (CReflectableForwardLinkedList *)Data.At(InForwardLinkedListType,i);
        for (auto& Node : *List)
        {
            void * Key = ((CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType)->GetKeyPtr(Node.GetData());
            void * Value = ((CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType)->GetValuePtr(Node.GetData());
            if(!InMap.Contains(InForwardLinkedListType,Key))
            {
                return false;
            }

            if(!((CPairGeneralized*)InForwardLinkedListType->GetElementType().BasicType)->IsValueEqual(Value,InMap.FindValue(InForwardLinkedListType,Key)))
            {
                return false;
            }
        }
    }
    return true;
}

void CReflectableMap::MakesureCapacity(CForwardLinkedListGeneralized *InForwardLinkedListType)
{
    if (Size <= Capacity)
    {
        return;
    }
	
    Capacity=Capacity << 1;
    assert(Capacity > Size);

    CReflectableVector NewData;
    NewData.Resize(InForwardLinkedListType,Capacity);

    for(uint64_t i=0;i<Data.Num();i++)
    {
        CReflectableForwardLinkedList * OldElement = (CReflectableForwardLinkedList *)Data.At(InForwardLinkedListType,i);
        if(OldElement->Empty())
        {
            continue;
        }

        uint64_t NewArrayIndex0 = i ;
        uint64_t NewArrayIndex1 = NewArrayIndex0 + (Capacity>>1);
        
        while(!OldElement->Empty())
        {
            CReflectableForwardLinkedListNode * Node = OldElement->PopNode();

            uint64_t Hash = InForwardLinkedListType->GetInstanceHash(OldElement);

            if ((Hash & (Capacity - 1)) == NewArrayIndex0)
            {
                ((CReflectableForwardLinkedList *)(NewData.At(InForwardLinkedListType,NewArrayIndex0)))->InsertAtHead(Node);
            }
            else
            {
                ((CReflectableForwardLinkedList *)(NewData.At(InForwardLinkedListType,NewArrayIndex0)))->InsertAtHead(Node);
            }
        }
    }

    Data.Clear(InForwardLinkedListType);
    Data = std::move(NewData);
    
    
}
